package com.kj.tms.common.utils.calx.formula;


import com.kj.tms.common.utils.NumberUtils;
import com.kj.tms.common.utils.StringUtil;
import com.kj.tms.common.utils.calx.sheet.Sheet;
import com.kj.tms.common.utils.calx.util.Utility;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @ProjectName: ledger
 * @Package: com.hongtu.tms.ledger.calx.factory.formula
 * @ClassName: Math
 * @Description: [excel函数]
 * @Author: [zhuyongjie]
 * @CreateDate: 2018/4/17 9:20
 * @UpdateUser: []
 * @UpdateDate: 2018/4/17 9:20
 * @UpdateRemark: []
 * @Version: 1.0
 * Copyright: 北京宏图天安 Copyright (c) 2018
 */
public class Math {
    private static Logger logger = LoggerFactory.getLogger(Math.class);
    /**
     * @Method      ABS
     * @Param		number
     * @Return      java.lang.String
     * @Exception
     * @Description [绝对值运算]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/4/20 16:12
     */
    public static String ABS(String number){
        BigDecimal bigDecimal = StringUtil.toBigDecimal(number);
        double abs = java.lang.Math.abs(bigDecimal.doubleValue());
//        System.out.println("Math.ABS("+number+") calculation result : " + abs);
        logger.info("Math.ABS("+number+") calculation result : " + abs);
        return StringUtil.toString(abs);
    }//ABS end


    /**
     * @Method      SUM
     * @Param		input
     * @Return      java.lang.String
     * @Exception
     * @Description [excel SUM运算]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/4/17 9:30
     */
    public static String SUM(List<Map<String,Object>> list){
        String result = "0";
        if(!StringUtil.isEmptyOrNull(list) && list.size() > 0){
            for(Map<String,Object> input : list){
                if(!StringUtil.isEmptyOrNull(input)){
                    BigDecimal bigValue = new BigDecimal(result);
                    for(Map.Entry<String,Object> entry : input.entrySet()){
                        BigDecimal value = StringUtil.toBigDecimal(entry.getValue());
                        bigValue = bigValue.add(value);
                    }//for end
                    result = bigValue.toString();
                }//if end
            }
        }//if end
//        System.out.println("Math.SUM("+input.toString()+") calculation result : " + result);
        logger.info("Math.SUM("+list.toString()+") calculation result : " + result);
        return result;
    }//SUM end

    /**
     * @Method      MOD
     * @Param		dividend 被除数
     * @Param		divisor 除数
     * @Return      double
     * @Exception
     * @Description [求余运算]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/4/17 16:39
     */
    public static double MOD(double dividend, double divisor) {
        double abs = java.lang.Math.abs(dividend % divisor);
        double result = divisor > 0 ? abs : (abs * -1);
//        System.out.println("Math.MOD("+dividend+","+divisor+") calculation result : " + result);
        logger.info("Math.MOD("+dividend+","+divisor+") calculation result : " + result);
        return result;
    }

    /**
     * @Method      POWER
     * @Param		number
     * @Param		power
     * @Return      double
     * @Exception
     * @Description [幂运算]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/4/17 16:50
     */
    public static double POWER(double number, double power) {
        double result = java.lang.Math.pow(number, power);
//        System.out.println("Math.POWER("+number+","+power+") calculation result : " + result);
        logger.info("Math.POWER("+number+","+power+") calculation result : " + result);
        return result;
    }

    /**
     * @Method      ROUND
     * @Param		number 数字
     * @Param		digits 精度
     * @Return      double
     * @Exception
     * @Description [四舍五入方法]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/4/17 17:05
     */
    public static double ROUND(double number,double digits){
        double result = java.lang.Math.round(number * java.lang.Math.pow(10, digits))
                / java.lang.Math.pow(10, digits);
//        System.out.println("Math.ROUND("+number+","+digits+") calculation result : " + result);
        logger.info("Math.ROUND("+number+","+digits+") calculation result : " + result);
        return result;
    }//ROUND end

    /**
     * @Method      ROUNDDOWN
     * @Param		number
     * @Param		digits 精度
     * @Return      double
     * @Exception
     * @Description [向下取舍]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/4/17 17:14
     */
    public static double ROUNDDOWN(double number,double digits) {
        int sign = number > 0 ? 1 : -1;
        double result = sign
                * (java.lang.Math.floor(java.lang.Math.abs(number) * java.lang.Math.pow(10, digits)))
                / java.lang.Math.pow(10, digits);
//        System.out.println("Math.ROUNDDOWN("+number+","+digits+") calculation result : " + result);
        logger.info("Math.ROUNDDOWN("+number+","+digits+") calculation result : " + result);
        return result;
    }//ROUNDDOWN end

    /**
     * @Method      ROUNDUP
     * @Param		number
     * @Param		digits 精度
     * @Return      java.lang.Object
     * @Exception
     * @Description [向上取舍]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/4/17 17:18
     */
    public static double ROUNDUP(double number,double digits) {
        int sign = number > 0 ? 1 : -1;
        double result = sign
                * (java.lang.Math.ceil(java.lang.Math.abs(number) * java.lang.Math.pow(10, digits)))
                / java.lang.Math.pow(10, digits);
//        System.out.println("Math.ROUNDUP("+number+","+digits+") calculation result : " + result);
        logger.info("Math.ROUNDUP("+number+","+digits+") calculation result : " + result);
        return result;
    }//ROUNDUP end

    /**
     * @Method      SUMIF
     * @Param		rangeMap 条件区域，用于条件判断的单元格区域
     * @Param		criteria 求和条件，由数字、逻辑表达式等组成的判定条件
     * @Param		sumRangeMap 实际求和区域，需要求和的单元格、区域或引用。
     * @Param		sheet sheet页对象
     * @Return      java.lang.Object
     * @Exception
     * @Description [SUMIF 函数]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/4/22 19:59
     */
    public static Double SUMIF(Map<String,Object> rangeMap, String criteria, Map<String,Object> sumRangeMap, Sheet sheet) throws Exception {
        double result = 0;
        String[] range = Utility.mapToArray(rangeMap);
        String[] sumRange = Utility.mapToArray(sumRangeMap);
        for(int i = 0;i < range.length;i ++){
            String evaluate = sheet.evaluate(range[i] + criteria);
            if(!StringUtil.isEmptyOrNull(evaluate)){
                evaluate = evaluate.toUpperCase();
                if(evaluate.equals("TRUE") || evaluate.equals("FALSE")){
                    boolean b = Boolean.parseBoolean(evaluate);
                    if(b){
                        if(i < sumRange.length){
                            String s = sumRange[i];
                            if(NumberUtils.isRealNumber(s)){
                                result += StringUtil.toDouble(s);
                            }
                        }
                    }
                }//if end
            }//if end
        }//for end
//        System.out.println("Math.SUMIF("+rangeMap.toString()+","+criteria+","+sumRangeMap+") calculation result : " + result);
        logger.info("Math.SUMIF("+rangeMap.toString()+","+criteria+","+sumRangeMap+") calculation result : " + result);
        return result;
    }

    /**
     * @Method      SUMIFS
     * @Param		sumifsList
     * @Param		sheet
     * @Return      java.lang.Double
     * @Exception
     * @Description [sumifs 计算逻辑]
     * 语法
     * SUMIFS(sum_range, criteria_range1, criteria1,
     * [criteria_range2, criteria2], ...)
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/4/23 10:30
     */
    public static Double SUMIFS(List sumifsList, Sheet sheet) throws Exception {

        double result = 0;
        int criteria = (sumifsList.size() - 1) / 2;
        String[] range = Utility.mapToArray((HashMap) sumifsList.get(0));//求和范围
        
        for(int i = 0;i < range.length;i ++){
            boolean fit = true;
            for (int j = 0;j < criteria;j ++){
                String criteria_clause = StringUtil.toString(sumifsList.get(2 * j + 2));//比较条件
                String[] criteria_range = Utility.mapToArray((HashMap) sumifsList.get(2 * j + 1));//比较范围
                if(fit){
                   if(i < criteria_range.length){
                       String evaluate = sheet.evaluate(criteria_range[i] + criteria_clause);
                       if(evaluate.equals("TRUE") || evaluate.equals("FALSE")){
                           fit = Boolean.parseBoolean(evaluate);
                       }//if end
                   }//if end
                }//if end
            }//for end
            result += fit ? StringUtil.toDouble(range[i]) : 0;
        }//for end
//        System.out.println("Math.SUMIFS("+sumifsList.toString()+") calculation result : " + result);
        logger.info("Math.SUMIFS("+sumifsList.toString()+") calculation result : " + result);
        return result;
    }

    /**
     * @Method      SUBTRACT
     * @Param		number1
     * @Param		number2
     * @Return      java.lang.Double
     * @Exception
     * @Description [减法]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/4/23 16:23
     */
    public static Double SUBTRACT(String number1, String number2) {
       double result = 0;

       if(StringUtil.isEmptyOrNull(number1)
               && StringUtil.isEmptyOrNull(number2)){
           return result;
       }//if end
       double v1 = 0;
       double v2 = 0;
       if(!StringUtil.isEmptyOrNull(number1)
               && NumberUtils.isRealNumber(number1)){
           v1 = StringUtil.toDouble(number1);
       }
       if(!StringUtil.isEmptyOrNull(number2)
                && NumberUtils.isRealNumber(number2)){
            v2 = StringUtil.toDouble(number2);
        }
        result = v1 - v2;
        logger.info("Math.SUBTRACT("+number1+","+number2+") calculation result : " + result);
        return result;
    }//SUBTRACT end

    /**
     * @Method      MULTIPLY
     * @Param		number1
     * @Param		number2
     * @Return      java.lang.Double
     * @Exception
     * @Description [乘法]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/4/23 16:33
     */
    public static Double MULTIPLY(String number1, String number2) {
        double result = 0;
        if(StringUtil.isEmptyOrNull(number1)
                && StringUtil.isEmptyOrNull(number2)){
            return result;
        }//if end
        double v1 = 0;
        double v2 = 0;
        if(!StringUtil.isEmptyOrNull(number1)
                && NumberUtils.isRealNumber(number1)){
            v1 = StringUtil.toDouble(number1);
        }
        if(!StringUtil.isEmptyOrNull(number2)
                && NumberUtils.isRealNumber(number2)){
            v2 = StringUtil.toDouble(number2);
        }
        result = v1 * v2;
        logger.info("Math.MULTIPLY("+number1+","+number2+") calculation result : " + result);
        return result;
    }//MULTIPLY end

    /**
     * @Method      DIVIDE
     * @Param		number1
     * @Param		number2
     * @Return      java.lang.Object
     * @Exception
     * @Description [除法运算]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/4/23 16:37
     */
    public static Object DIVIDE(String number1, String number2) {
        double result = 0;
        if(StringUtil.isEmptyOrNull(number1)
                && StringUtil.isEmptyOrNull(number2)){
            return result;
        }//if end
        double v1 = 0;
        double v2 = 0;
        if(!StringUtil.isEmptyOrNull(number1)
                && NumberUtils.isRealNumber(number1)){
            v1 = StringUtil.toDouble(number1);
        }
        if(!StringUtil.isEmptyOrNull(number2)
                && NumberUtils.isRealNumber(number2)){
            v2 = StringUtil.toDouble(number2);
        }
        if(v2 == 0){
            return "#DIV/0";
        }
        result = v1 / v2;
        logger.info("Math.DIVIDE("+number1+","+number2+") calculation result : " + result);
        return result;
    }//DIVIDE end
}
